﻿using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Controls.Primitives;
using System.Windows.Navigation;

using Heaven.Graphics;
using Heaven.Geometry.Procedural;
using Heaven.Geometry;
using Heaven.Mathematics;
using Heaven.Engine;
using Heaven.Engine.Animation;



using Color = Heaven.Graphics.Color;
using Colors = Heaven.Graphics.Colors;
using CameraBrowser = Heaven.Engine.CameraBrowser;
using Wavefront = Heaven.Geometry.Importers.Wavefront;
using Visual = Heaven.Engine.Visual;

namespace Heaven.Showcase.Demos
{
    /// <summary>
    /// Demonstrations. Construction demo
    /// </summary>
	public partial class Construction
    {
        #region Fields

        // Heaven device compatible with 
        // Windows Presentation Foundation
        //Heaven.Xna.Graphics.WindowsPresentationDevice device = null;
        Heaven.SlimDX.Graphics.WindowsPresentationDevice device = null;
        // Scene, as a container for objects
        Scene scene = null;

        // Camera with particular behaviour
        CameraBrowser camera = new CameraBrowser();
        // It's for dragging
        double mouseX = Double.NaN;
        double mouseY = Double.NaN;

        // Flag means that redraw needed
        bool invalidated = true;
        // Timer to perform rendering when invalidated is set
        DispatcherTimer timer = new DispatcherTimer(DispatcherPriority.Render);

        Visual toremove = null;

        #endregion

        #region Initializations

        /// <summary>
        /// Constructor
        /// </summary>
        public Construction()
		{
			this.InitializeComponent();
            this.Loaded += new RoutedEventHandler(OnLoaded);
            this.Unloaded += new RoutedEventHandler(OnUnloaded);
            targetImage.SizeChanged += new SizeChangedEventHandler(OnSizeChanged);
            targetImage.MouseMove += new MouseEventHandler(OnMouseMove);
            targetImage.MouseWheel += new MouseWheelEventHandler(OnMouseWheel);
            //targetImage.Width = targetImage.Height = 100;

            //System.Windows.Interop.ComponentDispatcher.ThreadIdle += new EventHandler(OnTimerTick);

            // Initialize the timer
            //timer.Interval = TimeSpan.FromMilliseconds(5);
            //timer.Tick += new EventHandler(OnTimerTick);
            //CompositionTarget.Rendering += new EventHandler(OnTimerTick);

            // Initialization of graphics device
            //device = new Heaven.Xna.Graphics.WindowsPresentationDevice(targetImage);
            device = new Heaven.SlimDX.Graphics.WindowsPresentationDevice(targetImage, Render);
            device.DefaultColor = new Color(0, 0, 0, 0);
            
            //device.AutoResize = false;
            //device.RenderStates.Wireframe = true;
            //device.RenderStates.CullMode = Heaven.Graphics.CullMode.CounterClockwise;

            //device.Resolution = new Heaven.Graphics.Size(500, 500);

            // Tuning the scene
            scene = new Scene(camera);
            camera.HorisontalRotation = 0.84f;
            camera.VerticalRotation = 1.07f;
            camera.Target = Vector3.Zero;
            camera.Distance = 70;
            LoadVisuals();


            PreviewMouseDown += delegate { device.Resolution = new Heaven.Graphics.Size(200, 200); };
            PreviewMouseUp += delegate { device.Resolution = new Heaven.Graphics.Size(Double.NaN, Double.NaN); };
            PreviewMouseDoubleClick += delegate { scene.Visuals.Remove(toremove); toremove = null; };

            Application.Current.MainWindow.Title = "0";
        }

        void OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            camera.Distance *= e.Delta > 0 ? 1.05 : 0.95;
            Invalidate();
        }

        #endregion

        #region Materials construction

        /*Technique CreateMaterial()
        {
            // Creating custom node
            
            InputValueNode input = new InputValueNode(SocketTypes.FloatFour);
            OutputValueNode output = new OutputValueNode(SocketTypes.FloatThree);
            
            CustomGPUNode gpunode = new CustomGPUNode();
            Socket inputSocket = new Socket(SocketTypes.FloatFour);
            Socket outputSocket = new Socket(SocketTypes.FloatThree);
            gpunode.Inputs.Add(inputSocket);
            gpunode.Outputs.Add(outputSocket);
            gpunode.Code = "float3 f = lod2d(.....";
             
            Graph graph = new Graph();
            graph.Nodes.Add(input);
            graph.Nodes.Add(output);
            graph.Nodes.Add(gpunode);
            graph.Connectors.Add(new Connector(input.Out, inputSocket));
            graph.Connectors.Add(new Connector(output.In, outputSocket));
            
            Technique result = new Technique(device);
            result.Pipeline.Add(Graph);
            result.Update();

            return result;
             
            
        }*/

        #endregion

        #region Manipulation routines

        void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point point = e.GetPosition(this);

                if (!Double.IsNaN(mouseX))
                {
                    camera.HorisontalRotation += (float)((point.X - mouseX) / 100);
                    camera.VerticalRotation += (float)((point.Y - mouseY) / 100);
                    Invalidate();
                }                
                
                mouseX = point.X;
                mouseY = point.Y;
            }
            else
            {
                mouseX = Double.NaN;
                mouseY = Double.NaN;
            }
        }

        void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            camera.Aspect = (float)(e.NewSize.Width / e.NewSize.Height);
            Invalidate();
        }

        void OnTimerTick(object sender, EventArgs e)
        {
           /* if (invalidated)
            {
                Render();
                invalidated = false;
            }*/
        }

        void OnUnloaded(object sender, RoutedEventArgs e)
        {
            //timer.Stop();
            //device.Dispose();
        }

        void OnLoaded(object sender, RoutedEventArgs e)
        {
            //timer.Start();
        }

        #endregion

        #region Loading geometries

        void LoadVisuals()
        {
            AxisAlignedBox aabb = new AxisAlignedBox();

            Sphere sphere = new Sphere(Vector3.Zero, 10);
            Polygonal spherePolygonal = sphere.Generate();
            toremove = AddVisual(spherePolygonal);
            aabb = spherePolygonal.AxisAlignedBoundingBox;
            Heaven.Geometry.Solid.AABBTree tree = Heaven.Geometry.Solid.AABBTree.Build(spherePolygonal);

            


            Cylinder cylinder = new Cylinder(new Vector3(23, -10, 0), 8, 25);
            cylinder.Direction = Vector3.Normalize(new Vector3(-0.5, 1, 0));
            Polygonal cylinderPolygonal = cylinder.Generate();           
            Visual cylVisual = AddVisual(cylinderPolygonal);
            cylVisual.Technique = new ColorTechnique();
            aabb.Accommodate(cylinderPolygonal.AxisAlignedBoundingBox);

            Cone cone = new Cone(new Vector3(0, 0, -20), 8, 3, 12);
            cone.Direction = Vector3.Normalize(new Vector3(1, 1, 1));
            Polygonal conePolygonal = cone.Generate();
            AddVisual(conePolygonal);
            aabb.Accommodate(conePolygonal.AxisAlignedBoundingBox);

            Cylinder disc = new Cylinder(new Vector3(0, -15, 0), 30, 1);
            Polygonal discPolygonal = disc.Generate();
            AddVisual(discPolygonal);
            aabb.Accommodate(discPolygonal.AxisAlignedBoundingBox);

            Polygonal circlePolygonal = Wireframe.GenerateDashedCircle(new Vector3(0, -15, 0), Vector3.YAxis, 30, 0.2, 0.1);
            Visual circleNode = AddVisual(circlePolygonal);
            circleNode.Technique = new WireframeTechnique();
            circleNode.Technique.Properties["Color"] = new Property<Color>(Heaven.Graphics.Colors.Yellow);

            Sphere hemisphere = new Sphere(new Vector3(0, 0, 18), 7);
            hemisphere.Quality = 0.5;
            hemisphere.Hemisphere = 0.75;
            Polygonal hemispherePolygonal = hemisphere.Generate();
            AddVisual(hemispherePolygonal);
            aabb.Accommodate(hemispherePolygonal.AxisAlignedBoundingBox);

            Box box = new Box(new Vector3(10, -10, 10), new Vector3(20, 0, 20));
            Polygonal boxPolygonal = box.Generate();
            AddVisual(boxPolygonal);
            aabb.Accommodate(boxPolygonal.AxisAlignedBoundingBox);

            Torus torus = new Torus(new Vector3(-20, 0, -20), 10, 3);
            torus.Direction = Vector3.Normalize(new Vector3(-1, 1, 0));
            Polygonal torusPolygonal = torus.Generate();
            AddVisual(torusPolygonal);
            aabb.Accommodate(torusPolygonal.AxisAlignedBoundingBox);

            AddParametricCurve();

            /*Node torusNode = AddVisual(torusPolygonal);
            torusNode.Technique = new WireframeTechnique();
            torusNode.Technique.Properties["Color"] = new ColorProperty(Colors.White);*/

            Polygonal wireframePolyline = Wireframe.GenerateDashedPolyline(new Vector3[]
            {
                new Vector3(10, -10, 10),
                new Vector3(10, -5, 20),
                new Vector3(-50, 0, -50),
                new Vector3(50, 0, -50),
                new Vector3(50, 0, 50),
                new Vector3(-50, 0, 50),
                new Vector3(-50, 50, -50),
                new Vector3(-50, -50, -50)
            }, 1, 1);
            Visual polylineVisual = AddVisual(wireframePolyline);
            polylineVisual.Technique = new WireframeTechnique(true);
            polylineVisual.Technique.Properties["Color"] = new Property<Color>(Colors.Violet);


            Polygonal normals = Debug.Normals(conePolygonal);
            AddVisual(normals);

            camera.Target = aabb.Center;
            camera.Distance = (aabb.Max - aabb.Min).Length() * 0.70;

            // Для теста
            /*FileStream stream = new FileStream("C:\\Polygonal.hpm", FileMode.Open);
            Polygonal badPolygonal = Polygonal.Deserialise(stream);
            stream.Close();

            AddVisual(badPolygonal);

            Spatial spatial = new Spatial(badPolygonal);

            Node nodeAABB = AddVisual(spatial.GenerateRepresentation());
            nodeAABB.Technique = new WireframeTechnique();*/

        }


        Visual AddVisual(Polygonal polygonal)
        {
            Visual visual = new Visual(polygonal);
            visual.Technique.Properties["Color"] = new Property<Color>(new Heaven.Graphics.Color(1.0, 0, 0, 1.0));
            scene.Visuals.Add(visual);
            return visual;
        }

        #endregion

        #region Parametric Curve Sample


        void AddParametricCurve()
        {
            Tessellator tessellator = new Tessellator();
            tessellator.Function = TorusEquation;
            tessellator.DerivativeU = dVTorus;
            tessellator.DerivativeV = dUTorus;

            tessellator.StartU = -Math.PI;
            tessellator.EndU = Math.PI;
            tessellator.StartV = -Math.PI;
            tessellator.EndV = Math.PI;
            tessellator.dV = 0.2;
            tessellator.dU = 0.5;

            Polygonal mesh = tessellator.Tesselate();
            Visual visualTorus = AddVisual(mesh);

            // Tesselate curve
            List<Vector3> points = new List<Vector3>();
            for (double t = -Math.PI; t < Math.PI; t += 0.01)
            {
                Vector2 uv = CurveEquation(t);
                points.Add(TorusEquation(uv.X, uv.Y));
            }

            Polygonal curveMesh = Wireframe.GeneratePolyline(points);
            Visual visualCurve = AddVisual(curveMesh);
            visualCurve.Technique = new WireframeTechnique();
            visualCurve.Technique.Properties["Color"] = new Property<Color>(Colors.Violet);

            // Tangent of curve at the point
            double differential = 0.01;
            double tparameter = 1;
            Vector2 curveUV = CurveEquation(tparameter);
            Vector2 dcurveUV = CurveEquation(tparameter + differential);

            Vector3 from = TorusEquation(curveUV.X, curveUV.Y);
            Vector3 to = TorusEquation(dcurveUV.X, dcurveUV.Y);
            Vector3 tangent = Vector3.Normalize(to - from);

            /*Vector2 curveUV = CurveEquation(0);
            Vector3 from = TorusEquation(curveUV.X, curveUV.Y);
            Vector3 tangent = Vector3.Normalize(TorusEquation(*/

            Polygonal tangentMesh = Wireframe.GenerateLine(from, from + tangent * 5.0);
            Visual visualTangent = AddVisual(tangentMesh);
            visualTangent.Technique = new WireframeTechnique(true);
            visualTangent.Technique.Properties["Color"] = new Property<Color>(Colors.White);
        }

        Vector2 CurveEquation(double t)
        {
            return new Vector2(Math.Sin(t*3), t);
        }

        Vector2 dtCurve(double t)
        {
            return new Vector2(Math.Cos(t * 3) * 3, 1);
        }

        Vector3 TorusEquation(double u, double v)
        {
            Vector3 Center = new Vector3(20, 0, -20);
            double Radius = 10;
            double SectionRadius = 3;

            Vector3 result = new Vector3();

            result.X = Center.X + (Radius + SectionRadius * Math.Cos(u)) * Math.Sin(v);
            result.Y = Center.Y + SectionRadius * Math.Sin(u);
            result.Z = Center.Z + (Radius + SectionRadius * Math.Cos(u)) * Math.Cos(v);

            return result;
        }

        Vector3 dUTorus(double u, double v)
        {
            Vector3 Center = new Vector3(-20, 0, -20);
            //double Radius = 10;
            double SectionRadius = 3;

            Vector3 result = new Vector3();

            result.X = -SectionRadius * Math.Sin(u) * Math.Sin(v);
            result.Z = -SectionRadius * Math.Sin(u) * Math.Cos(v);
            result.Y = SectionRadius * Math.Cos(u);

            return result;
        }

        Vector3 dVTorus(double u, double v)
        {
            Vector3 Center = new Vector3(-20, 0, -20);
            double Radius = 10;
            double SectionRadius = 3;

            Vector3 result = new Vector3();

            result.X = (Radius + SectionRadius * Math.Cos(u)) * Math.Cos(v);
            result.Z = -(Radius + SectionRadius * Math.Cos(u)) * Math.Sin(v);
            result.Y = 0.0;

            return result;
        }

        #endregion

        #region Rendering

        /*void OnRenderRequest(object sender, EventArgs e)
        {
            Render();
        }*/

        void Invalidate()
        {
            device.Invalidate();
        }

        void Render()
        {
            device.Begin();
            try
            {
                device.Clear();
                scene.Render(device);
            }
            finally
            {
                device.Present();
            }
        }
        
        #endregion
    }
}